/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package model.creature.monster;

import java.awt.Image;
import java.util.ArrayList;
import model.creature.moving_creature.ActionType;
import model.creature.moving_creature.Direction;
import model.game.GameInterface;
import model.map.GridMap;
import model.map.cell.Cell;

/**
 * Monster that can do ranged attacks
 */
public abstract class DefaultAOEAttackerSprite extends DefaultMonster {

  protected int next_move_;

  /**
   * Constructor
   * @param game - owner
   * @param cell - initial location
   */
  public DefaultAOEAttackerSprite(GameInterface game, Cell cell) {
    super(game, cell);
    next_move_ = 0;
  }

  @Override
  public void advanceTime() {
    --next_move_;
    if (next_move_ > 0) return;

    next_move_ = getSpeed();

    ArrayList< ActionType > best = new ArrayList<ActionType>();
    int bestimportance = 0;
    GridMap mainmap = getMap();
    for (int x = 0; x < mainmap.getGrid().getColumn(); ++x) {
      for (int y = 0; y < mainmap.getGrid().getRow(); ++y) {
        Cell cell = mainmap.getCellAt(x,y);
        if (howImportantIsFacingThis(cell) > 0) {
          for (int dir = 0; dir < Direction.getDirectionCount(); ++dir) {
            Cell target_cell = game_.getMaps().getCellInFrontOff(cell,
                    Direction.valueOf(dir));
            if (mainmap.isReachable(this,
                    getCell(), getDirection(), target_cell,
                    Direction.getReversedDirection(Direction.valueOf(dir)))) {
              ArrayList< ActionType > actions =
                      mainmap.getShortestPathBetween(this,
                      getCell(), getDirection(), target_cell,
                    Direction.getReversedDirection(Direction.valueOf(dir)));
              actions.add(getActionType(target_cell, Direction.getReversedDirection(Direction.valueOf(dir))));
              if (bestimportance < howImportantIsFacingThis(cell) ||
                      (bestimportance == howImportantIsFacingThis(cell) &&
                      best.size() > actions.size())) {
                bestimportance = howImportantIsFacingThis(cell);
                best = actions;
              }
            }
          }
        }
      }
    }

    for (int x = 0; x < mainmap.getGrid().getColumn(); ++x) {
      for (int y = 0; y < mainmap.getGrid().getRow(); ++y) {
        Cell cell = mainmap.getCellAt(x,y);
        for (int dir = 0; dir < Direction.getDirectionCount(); ++dir) {
          if (howImportantIsBeingHere(cell, Direction.valueOf(dir)) > 0) {
            Cell target_cell = cell;
            if (mainmap.isReachable(this, getCell(), getDirection(), target_cell,
                    Direction.valueOf(dir))) {
              ArrayList< ActionType > actions =
                      mainmap.getShortestPathBetween(this,
                      getCell(), getDirection(), target_cell,
                      Direction.valueOf(dir));
              actions.add(getActionType(target_cell, Direction.getReversedDirection(Direction.valueOf(dir))));
              if (bestimportance <
                      howImportantIsBeingHere(cell, Direction.valueOf(dir))
                      ||
                      (bestimportance == howImportantIsBeingHere(cell, Direction.valueOf(dir)) &&
                      best.size() > actions.size())) {
                bestimportance = howImportantIsBeingHere(cell, Direction.valueOf(dir));
                best = actions;
              }
            }
          }
        }
      }
    }

    if (best.size() != 1) {
      performAction(best.get(0));
    } else {
      ArrayList<Cell> targets = new ArrayList<Cell>();
      for (int x = 0; x < game_.getMaps().getField().getColumn(); ++x) {
        for (int y = 0; y < game_.getMaps().getField().getRow(); ++y) {
          if (Math.abs(this.getCell().getX() - x) + Math.abs(this.getCell().getY() - y) <= attackDistance()) {
            if (howImportantIsAttackingThis(game_.getMaps().getField().getCellAt(x,y)) > 0) {
              targets.add(game_.getMaps().getField().getCellAt(x,y));
            }
          }
        }
      }
      // sort targets
      for (int i = 0; i < targets.size(); ++i) {
        for (int j = i+1; j < targets.size(); ++j) {
          if (howImportantIsFacingThis(targets.get(i)) <
                  howImportantIsFacingThis(targets.get(j))) {
            Cell dummy = targets.get(i);
            targets.set(i, targets.get(j));
            targets.set(j, dummy);
          }
        }
      }

      int maxcnt = getAttackCount();
      for (int i = 0; i < targets.size(); ++i) {
        if (maxcnt <= 0) break;
        --maxcnt;
        game_.hostileAttack(this, targets.get(i), getPower());
      }
    }
  }

  @Override
  protected ActionType getActionType(Cell cell, Direction dir) {
    return ActionType.kActionStay;
  }

  @Override
  protected int howImportantIsFacingThis(model.map.cell.Cell cell) {
    return 0;
  }

  @Override
  protected int howImportantIsBeingHere(Cell cell, Direction dir) {
    int ret = 0;
    for (int x = 0; x < game_.getMaps().getField().getColumn(); ++x) {
      for (int y = 0; y < game_.getMaps().getField().getRow(); ++y) {
        if (Math.abs(cell.getX() - x) + Math.abs(cell.getY() - y) <= attackDistance()) {
          ret = Math.max(ret, howImportantIsAttackingThis(game_.getMaps().getField().getCellAt(x, y)));
        }
      }
    }
    return ret;
  }

  @Override
  public void advanceDay() {
    
  }

  /**
   * Gets maximum HP of monster at current level.
   *
   * To obtain the level, use getLevel(). Level starts at 0
   */
  @Override
  public abstract int getMaxHP();

  /**
   * Retrieves the name of this monster. Primary Key.
   */
  @Override
  public abstract String getName();

    /**
   * Retrieves the description of this monster
   */
  @Override
  public abstract String getDescription();

  /**
   * Return true if monster can be stepped on and step on unsteppable locations
   */
  @Override
  public abstract boolean isSteppable();

  /**
   * Can this creature steps on unsteppable cells?
   */
  @Override
  public abstract boolean canStepOnUnsteppableCells();

  /**
   * Return true if
   */
  @Override
  public abstract Image getImage();

  /**
   * Determines the damage done by a creature when attacking
   */
  @Override
  public abstract int getPower();

  /**
   * How important is for this creature to attack target cell?
   *
   * By default, sets crystals as most important targets,
   * plants as the second
   *
   * 0 means not important,
   * the greater the return value, the more important.
   */
  public int howImportantIsAttackingThis(Cell cell) {
    if (game_.getCreatures().getCrystals().containsCrystalAt(cell)) return 2;
    if (game_.getCreatures().getPlants().containsPlantAt(cell)) return 1;
    return 0;
  }

  /**
   * How far can this sprite attack?
   *
   * If this sprite can attack D distance, then monsters located at (x, y)
   * can be attacked by this creature at (X, Y) such that
   * |X - x| + |Y - y| <= D. (Manhattan Distance)
   */
  public abstract int attackDistance();

  /**
   * How many creatures can this monster attack during one action?
   *
   * If more than one creature is specified, then this creature will attack
   * multiple creature in sight.
   */
  public abstract int getAttackCount();

  /**
   * Get the speed of this monster
   *
   * This is the number of turns the monster must wait before attacking again.
   * 1 is the fastest (acts every turn), 2 acts every other turn, etc.
   */
  public abstract int getSpeed();
}
